#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>


/*
    僵尸进程 & 子进程给父进程发信号(通过kill(getppid(), sig)) & 子进程调用exit()，发送SIGCHLD信号给父进程
    命令: ./z+process
    现象：父进程正常运行，收到一次子进程的SIGUSR1信号
         子进程不调用exit()函数结束，则变成僵尸进程Z+
         子进程调用exit()函数结束，则向父进程发送SIGCHLD信号，父进程信号处理函数调用wait()函数回收子进程资源
*/

void handler(int arg)
{
    int statue;
    printf("信号处理handler，arg = %d\n", arg);  //arg是信号ID
    if(arg == SIGINT)
        printf("你按了 ctrl+c \n");
    if(arg == SIGUSR1)
        printf("父进程收到子进程信号\n");
    if(arg == SIGCHLD){
        printf("子进程终止时会向父进程发送SIGCHLD信号，告知父进程回收自己\n");
        wait(&statue);   //回收子进程资源

        /* 判断子进程是否正常退出
        1. WIFEXITED(status) 这个宏用来指出子进程是否为正常退出的，如果是，它会返回一个非零值。
            （请注意，虽然名字一样，这里的参数status并不同于wait唯一的参数–指向整数的指针status，
            而是那个指针所指向的整数，切记不要搞混了。）
        2. WEXITSTATUS(status) 当WIFEXITED返回非零值时，我们可以用这个宏来提取子进程的返回值，
            如果子进程调用exit(5)退出，WEXITSTATUS(status) 就会返回5；如果子进程调用exit(7)，WEXITSTATUS(status)就会返回7。
            请注意，如果进程不是正常退出的，也就是说， WIFEXITED返回0，这个值就毫无意义。
        */
        if(WIFEXITED(statue) != 0){
            printf("子进程正常退出\n");
        }
        if(WIFEXITED(statue) != 0){
            printf("statue = %d\n", WEXITSTATUS(statue));
        }
    }
}

int main(int argc, char *argv[])
{
    signal(SIGINT, handler);   //SIGINT（ctrl+c）信号处理

    pid_t pid;
    pid = fork();

    if(pid > 0){
        signal(SIGUSR1, handler);  //接收子进程的信号
        signal(SIGCHLD, handler);  //接收子进程结束的信号
        printf("父进程\n");
        for(int i = 0;i < 10;i++){
            printf("父 %d\n",i);
            sleep(1);
        }
    }
    if(pid == 0){
        printf("子进程运行，接着睡眠2秒\n");
        sleep(2);
        printf("给父进程发信号\n");
        kill(getppid(), SIGUSR1);

        sleep(1);
        printf("子进程自己隔屁了，子进程状态变化，自动向父进程发送SIGCHLD信号\n");
        // exit(8);
    }

    return 0;
}
